1 /** 2 * Singly-linked list. 3 * Copyright: © 2015 Economic Modeling Specialists, Intl. 4 * Authors: Brian Schott 5 * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) 6 */ 7 8 module containers.slist; 9 10 private import containers.internal.node : shouldAddGCRange; 11 private import std.experimental.allocator.mallocator : Mallocator; 12 13 /** 14 * Single-linked allocator-backed list. 15 * Params: 16 * T = the element type 17 * Allocator = the allocator to use. Defaults to `Mallocator`. 18 * supportGC = true if the container should support holding references to 19 * GC-allocated memory. 20 */ 21 struct SList(T, Allocator = Mallocator, bool supportGC = shouldAddGCRange!T) 22 { 23 /// Disable copying. 24 this(this) @disable; 25 26 private import std.experimental.allocator.common : stateSize; 27 28 static if (stateSize!Allocator != 0) 29 { 30 /// No default construction if an allocator must be provided. 31 this() @disable; 32 33 /** 34 * Use the given `allocator` for allocations. 35 */ 36 this(Allocator allocator) 37 in 38 { 39 assert(allocator !is null, "Allocator must not be null"); 40 } 41 do 42 { 43 this.allocator = allocator; 44 } 45 } 46 47 ~this() 48 { 49 Node* current = _front; 50 Node* prev = null; 51 while (current !is null) 52 { 53 prev = current; 54 current = current.next; 55 typeid(Node).destroy(prev); 56 static if (useGC) 57 { 58 import core.memory : GC; 59 GC.removeRange(prev); 60 } 61 allocator.dispose(prev); 62 } 63 _front = null; 64 } 65 66 /** 67 * Complexity: O(1) 68 * Returns: the most recently inserted item 69 */ 70 auto front(this This)() inout @property 71 in 72 { 73 assert (!empty, "Accessing .front of empty SList"); 74 } 75 do 76 { 77 alias ET = ContainerElementType!(This, T); 78 return cast(ET) _front.value; 79 } 80 81 /** 82 * Complexity: O(length) 83 * Returns: the least recently inserted item. 84 */ 85 auto back(this This)() inout @property 86 in 87 { 88 assert (!empty, "Accessing .back of empty SList"); 89 } 90 do 91 { 92 alias ET = ContainerElementType!(This, T); 93 94 auto n = _front; 95 for (; front.next !is null; n = n.next) {} 96 return cast(ET) n.value; 97 } 98 99 /** 100 * Removes the first item in the list. 101 * 102 * Complexity: O(1) 103 * Returns: the first item in the list. 104 */ 105 T moveFront() 106 in 107 { 108 assert (!empty, "Accessing .moveFront of empty SList"); 109 } 110 do 111 { 112 Node* f = _front; 113 _front = f.next; 114 T r = f.value; 115 static if (useGC) 116 { 117 import core.memory : GC; 118 GC.removeRange(f); 119 } 120 allocator.dispose(f); 121 --_length; 122 return r; 123 } 124 125 /** 126 * Removes the first item in the list. 127 * 128 * Complexity: O(1) 129 */ 130 void popFront() 131 { 132 Node* f = _front; 133 _front = f.next; 134 static if (useGC) 135 { 136 import core.memory : GC; 137 GC.removeRange(f); 138 } 139 allocator.dispose(f); 140 --_length; 141 } 142 143 /** 144 * Complexity: O(1) 145 * Returns: true if this list is empty 146 */ 147 bool empty() inout pure nothrow @property @safe @nogc 148 { 149 return _front is null; 150 } 151 152 /** 153 * Complexity: O(1) 154 * Returns: the number of items in the list 155 */ 156 size_t length() inout pure nothrow @property @safe @nogc 157 { 158 return _length; 159 } 160 161 /** 162 * Inserts an item at the front of the list. 163 * 164 * Complexity: O(1) 165 * Params: t = the item to insert into the list 166 */ 167 void insertFront(T t) @trusted 168 { 169 _front = make!Node(allocator, _front, t); 170 static if (useGC) 171 { 172 import core.memory : GC; 173 GC.addRange(_front, Node.sizeof); 174 } 175 _length++; 176 } 177 178 /// ditto 179 alias insert = insertFront; 180 181 /// ditto 182 alias insertAnywhere = insertFront; 183 184 /// ditto 185 alias put = insertFront; 186 187 /** 188 * Supports `list ~= item` syntax 189 * 190 * Complexity: O(1) 191 */ 192 void opOpAssign(string op)(T t) if (op == "~") 193 { 194 put(t); 195 } 196 197 /** 198 * Removes the first instance of value found in the list. 199 * 200 * Complexity: O(length) 201 * Returns: true if a value was removed. 202 */ 203 bool remove(V)(V value) @trusted 204 { 205 Node* prev = null; 206 Node* cur = _front; 207 while (cur !is null) 208 { 209 if (cur.value == value) 210 { 211 if (prev !is null) 212 prev.next = cur.next; 213 if (_front is cur) 214 _front = cur.next; 215 static if (shouldAddGCRange!T) 216 { 217 import core.memory : GC; 218 GC.removeRange(cur); 219 } 220 allocator.dispose(cur); 221 _length--; 222 return true; 223 } 224 prev = cur; 225 cur = cur.next; 226 } 227 return false; 228 } 229 230 /** 231 * Forward range interface 232 */ 233 auto opSlice(this This)() inout 234 { 235 return Range!(This)(_front); 236 } 237 238 /** 239 * Removes all elements from the range 240 * 241 * Complexity: O(length) 242 */ 243 void clear() 244 { 245 Node* prev = null; 246 Node* cur = _front; 247 while (cur !is null) 248 { 249 prev = cur; 250 cur = prev.next; 251 static if (shouldAddGCRange!T) 252 { 253 import core.memory : GC; 254 GC.removeRange(prev); 255 } 256 allocator.dispose(prev); 257 } 258 _front = null; 259 _length = 0; 260 } 261 262 private: 263 264 import std.experimental.allocator : make, dispose; 265 import containers.internal.node : shouldAddGCRange; 266 import containers.internal.element_type : ContainerElementType; 267 import containers.internal.mixins : AllocatorState; 268 269 enum bool useGC = supportGC && shouldAddGCRange!T; 270 271 static struct Range(ThisT) 272 { 273 public: 274 ET front() pure nothrow @property @trusted @nogc 275 { 276 return cast(typeof(return)) current.value; 277 } 278 279 void popFront() pure nothrow @safe @nogc 280 { 281 current = current.next; 282 } 283 284 bool empty() const pure nothrow @property @safe @nogc 285 { 286 return current is null; 287 } 288 289 private: 290 alias ET = ContainerElementType!(ThisT, T); 291 const(Node)* current; 292 } 293 294 static struct Node 295 { 296 Node* next; 297 T value; 298 } 299 300 mixin AllocatorState!Allocator; 301 Node* _front; 302 size_t _length; 303 } 304 305 version(emsi_containers_unittest) unittest 306 { 307 import std..string : format; 308 import std.algorithm : canFind; 309 SList!int intList; 310 foreach (i; 0 .. 100) 311 intList.put(i); 312 assert(intList.length == 100, "%d".format(intList.length)); 313 assert(intList.remove(10)); 314 assert(!intList.remove(10)); 315 assert(intList.length == 99); 316 assert(intList[].canFind(9)); 317 assert(!intList[].canFind(10)); 318 SList!string l; 319 l ~= "abcde"; 320 l ~= "fghij"; 321 assert (l.length == 2); 322 } 323 324 version(emsi_containers_unittest) unittest 325 { 326 static class Foo 327 { 328 string name; 329 } 330 331 SList!Foo hs; 332 auto f = new Foo; 333 hs.put(f); 334 }